package com.share.common.utils;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 密钥文件:
 * BEGIN     PRIVATE KEY 是 PKCS#8
 * BEGIN RSA PRIVATE KEY 是 PKCS#1
 *
 * <p>
 * 证书的两种文件格式
 * der后缀文件是二进制
 * pem文件是文本文件,内容是base64
 *
 *
 * <p>
 * 公钥是X509EncodedKeySpec，私钥是PKCS8EncodedKeySpec
 * 备注 数据量太大，加密解密会报错，此时要用到分断加密
 * <p>
 * Pfx证书，同时包含了公钥信息和私钥信息，PFX也称为PKCS#12
 * Cer证书只包含公钥信息，一般只能用于解密使用
 * Pfx证书既可以导出为pfx证书（包含私钥和公钥）---需要指定一个密码（后续读取会用到），也可以导出为cer证书（公钥）
 */
public class RSAUtil {
    //用于缓存证书文件
    private static Map<String, ?> fileMap = new HashMap<>();
    //PKF文件
    private final static String KEY_STORE_PKCS12 = "PKCS12";

    //签名
    private final static String SIGNATURE_MD5_RSA = "MD5withRSA";
    private final static String SIGNATURE_SHA1_RSA = "SHA1WithRSA";
    private final static String SIGNATURE_SHA256_RSA = "SHA256WithRSA";

    //KEYFACTORY 相关参数
    private final static String KEY_FACTORY_RSA = "RSA";


    //CHIPER 相关参数
    private final static String CHIPER_RSA_ECB_PKCS1PADDING = "RSA/ECB/PKCS1Padding";
    private final static String CHIPER_RSA = "RSA";

    //对内容进行切割
    public static String split16(String key) {
        final String rn = System.lineSeparator();
        if (key == null) {
            return null;
        } else if (key.length() <= 16) {
            return key;
        } else {
            int len = key.length();
            int line = len / 64 + ((len % 64 == 0) ? 0 : 1);
            StringBuffer sb = new StringBuffer(key);
            for (int i = line - 1; i > 0; i--) {
                sb.insert(i * 64, rn);
            }
            return sb.toString();
        }
    }

    //生产rsa的密钥对  keySize取值为96-1024位
    public static Map<String, String> genRSAKey(int keySize) {
        KeyPairGenerator keyPairGenerator = null;
        try {
            keyPairGenerator = KeyPairGenerator.getInstance(CHIPER_RSA);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        // 初始化密钥对生成器，密钥大小为96-1024位
        keyPairGenerator.initialize(keySize, new SecureRandom());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        Base64.Encoder encoder = Base64.getEncoder();
        String base64PriKey = encoder.encodeToString(keyPair.getPrivate().getEncoded());
        String base64PubKey = encoder.encodeToString(keyPair.getPublic().getEncoded());
        Map<String, String> reMap = new HashMap<>();
        reMap.put("privateKey", base64PriKey);
        reMap.put("publicKey", base64PubKey);
        return reMap;
    }

    //读取-公钥文件
    public static PublicKey getPublicKeyFromFile(String publicKeyFilePath) {
        String read = FileUtil.read(new File(publicKeyFilePath));
        return getPublicKeyByText(read);
    }

    //封装-公钥对象
    private static PublicKey getPublicKeyByText(String pubKeyText) {
        byte[] keyByte = Base64.getDecoder().decode(pubKeyText);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyByte);
        try {
            KeyFactory instance = KeyFactory.getInstance(KEY_FACTORY_RSA);
            return instance.generatePublic(spec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //读取-私钥文件
    public static PrivateKey getPrivateKeyFromFile(String privateKeyFilePath) {
        String read = FileUtil.read(new File(privateKeyFilePath));
        return getPrivateKeyByText(read);
    }

    //封装-私钥对象
    private static PrivateKey getPrivateKeyByText(String privateKey) {
        byte[] keyByte = Base64.getDecoder().decode(privateKey);
        try {
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyByte);
            KeyFactory instance = KeyFactory.getInstance(KEY_FACTORY_RSA);
            return instance.generatePrivate(spec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //获取pfx文件---获取私钥
    public static PrivateKey getPrivateKeyFromPfx(String pfxFilePath, String priKeyPassword) {
        PrivateKey key = readPfxFile(pfxFilePath, priKeyPassword, PrivateKey.class);
        return key;
    }

    //读取pfx文件---获取公钥
    public static PublicKey getPublicKeyFromPfx(String pfxFilePath, String priKeyPassword) {
        PublicKey key = readPfxFile(pfxFilePath, priKeyPassword, PublicKey.class);
        return key;
    }

    //读取pfx文件
    private static <T> T readPfxFile(String pfxFilePath, String priKeyPassword, Class<T> clazz) {
        T res = null;
        try {
            byte[] pfxBytes = FileUtil.readByte(pfxFilePath);
            char[] pwChar = priKeyPassword.toCharArray();
            KeyStore ks = KeyStore.getInstance(KEY_STORE_PKCS12);
            ks.load(new ByteArrayInputStream(pfxBytes), pwChar);

            Enumeration<String> aliases = ks.aliases();
            if (aliases.hasMoreElements()) {
                String keyAlias = aliases.nextElement();
                if (PrivateKey.class.equals(clazz)) {
                    res = (T) ks.getKey(keyAlias, pwChar);//获取私钥
                } else if (PublicKey.class.equals(clazz)) {
                    X509Certificate x509 = (X509Certificate) ks.getCertificate(keyAlias); //获取证书再获取私钥
                    res = (T) x509.getPublicKey();
                } else if (X509Certificate.class.equals(clazz)) {
                    res = (T) (X509Certificate) ks.getCertificate(keyAlias); //获取证书
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    //用私钥 对特定的内容 进行签名
    public static String sign(String content, String privateKey) {
        String res = null;
        try {
            PrivateKey key = getPrivateKeyByText(privateKey);
            Signature signature = Signature.getInstance(SIGNATURE_SHA256_RSA);
            signature.initSign(key);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            byte[] sign = signature.sign();
            res = Base64.getEncoder().encodeToString(sign);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    //公钥 用特定的内容和签名校验比对, 验证数据的有效性
    public static boolean verify(String content, String base64Sign, String pubKeyText) {
        boolean res = false;
        try {
            PublicKey key = getPublicKeyByText(pubKeyText);
            Signature signature = Signature.getInstance(SIGNATURE_SHA256_RSA);
            signature.initVerify(key);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            res = signature.verify(Base64.getDecoder().decode(base64Sign));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    //用公钥内容加密
    public static String encrypt(String src, String pubKeyText) {
        String res = null;
        PublicKey key = getPublicKeyByText(pubKeyText);
        return encrypt(src, (PublicKey) key);
    }

    public static String encrypt(String src, PublicKey key) {
        String res = null;
        try {
            byte[] encryptedBytes = cipher(src.getBytes(StandardCharsets.UTF_8), key, Cipher.ENCRYPT_MODE);
            res = Base64Util.encode(encryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    //根据私钥文件解密
    public static String decrypt(String base64Str, String privateKey) {
        String res = null;
        PrivateKey key = getPrivateKeyByText(privateKey);
        byte[] bData = Base64.getDecoder().decode(base64Str);
        return decrypt(base64Str, key);
    }

    //根据私钥文件解密
    public static String decrypt(String base64Str, PrivateKey key) {
        String res = null;
        try {
            byte[] bData = Base64.getDecoder().decode(base64Str);
            byte[] encryptedBytes = cipher(bData, key, Cipher.DECRYPT_MODE);
            res = new String(encryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    //通用处理----如果元数据太长了,需要分段加密
    public static byte[] cipher(byte[] data, Key key, int mode) throws Exception {
        Cipher cipher = Cipher.getInstance(CHIPER_RSA);
        cipher.init(mode, key);

        //不分组直接调用cipher.doFinal(src)即可,非常简单

        //获取块大小
        int inputLen = data.length;
        int block_size = (mode == Cipher.ENCRYPT_MODE) ? cipher.getOutputSize(inputLen) - 11 : cipher.getOutputSize(inputLen);

        //分组处理
        byte[] cache;
        int offSet = 0, i = 0;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > block_size) {
                cache = cipher.doFinal(data, offSet, block_size);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            i++;
            offSet = i * block_size;
            out.write(cache, 0, cache.length);
        }
        byte[] bytes = out.toByteArray();
        out.close();
        return bytes;
    }

    public static void main(String[] args) {
        Map<String, String> map = genRSAKey(1024);
        String text = "FdpublicKey -> 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";
        String privateKey = encrypt(text, map.get("publicKey"));
        String publicKey1 = decrypt(privateKey, map.get("privateKey"));
        System.out.println(publicKey1);
    }
}
